#include "stdafx.h"
#include "..\headers\Core.h"
#include "..\headers\StateManager.h"
#include "..\headers\PhysicsWorld.h"
#include "..\headers\GameObject.h"
#include "..\headers\InputSystem.h"
#include "..\headers\GameTime.h"
#include "..\headers\LuaEngine.h"
#include "..\headers\ComponentsPrereq.h"

namespace ne{

Core::Core(void) : 
	m_root(nullptr), 
	m_stateManager(nullptr), 
	m_renderWindow(nullptr), 
	m_physicsWorld(nullptr),
	m_inputSystem(nullptr),
	m_luaEngine(nullptr),
	m_gameTime(nullptr),
	m_run(true){}
Core::~Core(void){}

bool Core::Init(){
	//temp
	//tinyxml2::XMLDocument tempDoc;
	//tempDoc.LoadFile("system.xml");
	//int tempSize = System::FileSize("system.xml");
	//char* tempChar = new char[tempSize];
	//System::FileRead("system.xml", tempChar, tempSize);
	//System::FileSave("system.dat",tempChar,tempSize);
	// end of temp

	// Load Button settings
	
	Ogre::String systemData = "data/system.dat";

	int fileSize = System::FileSize(systemData);
	char* xml = new char[fileSize];
	if (!System::FileRead(systemData, xml, fileSize)){
		System::Notify("Failed to read system.dat, this file might be missing or is corrupt","File read error");
		return false;
	}
	tinyxml2::XMLDocument doc;
	doc.Parse(xml,fileSize);
	delete[] xml;

	m_root = OGRE_NEW Ogre::Root;
#ifdef _DEBUG
	m_root->loadPlugin(".\\RenderSystem_Direct3D9_d");
	m_root->loadPlugin(".\\RenderSystem_Direct3D11_d");
	m_root->loadPlugin(".\\RenderSystem_GL_d");
	m_root->loadPlugin(".\\Plugin_CgProgramManager_d");
	m_root->loadPlugin(".\\Plugin_OctreeSceneManager_d");
	m_root->loadPlugin(".\\Plugin_ParticleFX_d");
	//m_root->loadPlugin(".\\OgreOggSound_d"); Not yet implemented
#else
	m_root->loadPlugin(".\\RenderSystem_Direct3D9");
	m_root->loadPlugin(".\\RenderSystem_Direct3D11");
	m_root->loadPlugin(".\\RenderSystem_GL");
	m_root->loadPlugin(".\\Plugin_CgProgramManager");
	m_root->loadPlugin(".\\Plugin_OctreeSceneManager");
	m_root->loadPlugin(".\\Plugin_ParticleFX");
	//m_root->loadPlugin(".\\OgreOggSound"); Not yet implemented
#endif
	if (!m_root->showConfigDialog()){
		return false;
	}

	m_renderWindow = m_root->initialise(true, "NEngine");
	if (m_renderWindow){
		Ogre::WindowEventUtilities::addWindowEventListener(m_renderWindow, this);
	}
	else {
		System::Notify("Could not initialize root system and/or window","System initialization error");
		return false;
	}

	// Read from system.dat and add resource locations accordingly
	tinyxml2::XMLElement* element = doc.FirstChildElement("filepaths")->FirstChildElement("path");
	while (element != nullptr){
		Ogre::String dir = element->Attribute("dir");
		Ogre::String dirType = element->Attribute("type");
		m_root->addResourceLocation(dir,dirType,"General");
		element = element->NextSiblingElement();
	}
	System::SetLogFile("NEngine", "log");

	// Root and render window has been initialized so let's also initialize resource groups before moving any further.
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
	m_root->getTextureManager()->setDefaultNumMipmaps(3);
	//Base systems memory allocation
	m_physicsWorld = new PhysicsWorld;
	m_inputSystem = new InputSystem;
	m_gameTime = new GameTime;
	m_luaEngine = new LuaEngine;
	m_stateManager = new StateManager;

	m_inputSystem->_Init(m_renderWindow);
	m_physicsWorld->Init();
	
	element = doc.FirstChildElement("controls")->FirstChildElement("button");
	while (element != nullptr){
		Ogre::String buttonName = element->Attribute("name");
		int type = atoi(element->Attribute("type"));
		int id = atoi(element->Attribute("id"));
		m_inputSystem->AddButton(buttonName,type,id);
		element = element->NextSiblingElement();
	}
	element = doc.FirstChildElement("states")->FirstChildElement("state");
	while (element != nullptr){
		Ogre::String stateName = element->Attribute("name");
		Ogre::String stateClassName = element->Attribute("class");
		m_stateManager->AddState(new State(stateName,stateClassName,m_renderWindow,m_inputSystem,m_luaEngine,m_physicsWorld));
		element = element->NextSiblingElement("state");
	}
	element = doc.FirstChildElement("states")->FirstChildElement("default");
	Ogre::String defaultState = element->Attribute("name");

	// Before we Init lua engine we check where it will look for scripts
	element = doc.FirstChildElement("scriptpaths")->FirstChildElement("path");
	if (element == nullptr){
		System::Notify("No script paths detected in system.dat. Exiting...","File read error");
		return false;
	}
	while (element != nullptr) {
		Ogre::String scriptPath = element->Attribute("dir");
		m_luaEngine->AddPath(scriptPath);
		element = element->NextSiblingElement("path");
	}
	m_luaEngine->Init(m_physicsWorld,m_stateManager,m_inputSystem,m_gameTime);
	// initialize and set up global LUA systems for scripts

	GameObject::SetBaseSystems(m_physicsWorld,m_inputSystem);
	m_stateManager->ChangeState(m_stateManager->FindByName(defaultState));
	return true;
}

void Core::Run(){
	m_root->clearEventTimes();
	while (m_run){
		Ogre::WindowEventUtilities::messagePump();
		m_gameTime->_ResetElapsedTime();
		if (m_gameTime->_UpdateAndRender()){
			m_inputSystem->_Capture();
			if (!m_stateManager->Update(m_gameTime)){m_run=false;}
			m_physicsWorld->Step(*m_gameTime);
			IComponentLateUpdate::LateUpdateAllComponent(m_gameTime);
			if (!m_root->renderOneFrame(m_gameTime->DeltaTime())){m_run=false;}
		}
	}
}

void Core::Shut(){
	if (m_stateManager){
		m_stateManager->Shut();
		delete m_stateManager;
		m_stateManager = nullptr;
	}
	if (m_gameTime){
		delete m_gameTime;
		m_gameTime = nullptr;
	}
	if (m_luaEngine){
		m_luaEngine->Shut();
		delete m_luaEngine;
		m_luaEngine = nullptr;
	}
	if (m_inputSystem){
		m_inputSystem->_Shut();
		delete m_inputSystem;
		m_inputSystem = nullptr;
	}
	if (m_physicsWorld){
		m_physicsWorld->Shut();
		delete m_physicsWorld;
		m_physicsWorld = nullptr;
	}
	if (m_renderWindow){
		Ogre::WindowEventUtilities::removeWindowEventListener(m_renderWindow, this);
	}
	if (m_root){
		OGRE_DELETE m_root;
		m_root = nullptr;
		m_renderWindow = nullptr;
	}
}

void Core::windowResized(Ogre::RenderWindow* render_window){

}

void Core::windowClosed(Ogre::RenderWindow* render_window){
	m_run = false;
}

}